#include "StdAfx.h"
#include "M2Info.h"
#include "M2InfoCache.h"
#include "M2InstanceLoader.h"
#include "M2BatchRender.h"
#include "M2VideoLoadTask.h"
#include "M2Manager.h"

D3DVERTEXELEMENT9 M2BatchRender::ElemDecl[] = {
	{ 0, 0, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_POSITION, 0 },
	{ 0, 12, D3DDECLTYPE_FLOAT3, 0, D3DDECLUSAGE_NORMAL, 0 },
	{ 0, 24, D3DDECLTYPE_FLOAT2, 0, D3DDECLUSAGE_TEXCOORD, 0 },
	{ 1, 0, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 1 },
	{ 1, 16, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 2 },
	{ 1, 32, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 3 },
	{ 1, 48, D3DDECLTYPE_FLOAT4, 0, D3DDECLUSAGE_TEXCOORD, 4 },
	{ 0xFF, 0, D3DDECLTYPE_UNUSED, 0, 0, 0 }
};

M2BatchRender::M2BatchRender(const std::string& modelName) : mDecl(NULL),
	mModelInfo(sM2Info.getInfo(modelName)), mInstanceDataBuffer(NULL), mNumInstances(0),
	mInstanceLoader(this), mModelName(modelName), mVertexBuffer(NULL), mIndexBuffer(NULL)
{
	mLoadTask = new	M2VideoLoadTask(this, mModelInfo);
	sD3D.getTaskMgr().pushTask(mLoadTask);
	if(FAILED(sD3D->CreateVertexDeclaration(ElemDecl, &mDecl))) {
		throw std::exception("Unable to create vertex declaration!");
	}

	std::stringstream strm;
	strm << "Loading model: '" << modelName << "'...";
	sHud.SetText(strm.str().c_str(), 2000);
}

ui32 M2BatchRender::addInstance(float x, float y, float z, float scale, float rx, float ry, float rz) {
	return mInstanceLoader.pushInstance(D3DXVECTOR3(x, y, z), scale, D3DXVECTOR3(rx, ry, rz));
}

void M2BatchRender::renderInstance() {
	mInstanceLoader.updateVisibility();
	//if(mNumInstances == 0 || mMeshes.size() == 0 || (mMeshes.size() != mTextures.size()))
	//	return;
	if(mTextures.size() == 0 || mVertexBuffer == NULL || mIndexBuffer == NULL)
		return;

	sD3D->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	sD3D->SetRenderState(D3DRS_ALPHAREF, FToDW(0.001f));
	sD3D->SetRenderState(D3DRS_ALPHATESTENABLE, TRUE); 
	sD3D->SetRenderState(D3DRS_ALPHAFUNC, D3DCMP_GREATEREQUAL);

	for(ui32 i = 0; i < mModelInfo.getNumPasses(); ++i) {
		auto pass = mModelInfo.getPass(i);
		auto shdr = sM2Mgr.getM2Shader();
		shdr.setValue("MeshTexture", *mTextures[i]->GetTexPointer());

		shdr.doBasicRender(
			[this, i, pass]() {
				sD3D->SetVertexDeclaration(mDecl);
				sD3D->SetStreamSource(0, mVertexBuffer, 0, sizeof(MdxVertex));
				sD3D->SetStreamSourceFreq(0, mNumInstances | D3DSTREAMSOURCE_INDEXEDDATA);
				sD3D->SetStreamSource(1, mInstanceDataBuffer, 0, sizeof(D3DXMATRIX) + 4);
				sD3D->SetStreamSourceFreq(1, 1 | D3DSTREAMSOURCE_INSTANCEDATA);
				sD3D->SetIndices(mIndexBuffer);
				sD3D->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, mModelInfo.getNumVertices(), pass.startIndex,  pass.nIndices / 3);
		}
		);
	}

	sD3D->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
	sD3D->SetVertexDeclaration(NULL);
	sD3D->SetStreamSourceFreq(0, 1);
	sD3D->SetStreamSourceFreq(1, 1);
}

bool M2BatchRender::removeInstance(ui32 id) {
	mInstanceLoader.removeInstance(id);
	if(mInstanceLoader.isEmpty()) {
		mNumInstances = 0;
		for(ui32 i = 0; i < mTextures.size(); ++i) {
			mTextures[i]->DecRef();
		}

		mTextures.clear();
		if(mVertexBuffer != NULL)
			mVertexBuffer->Release();
		if(mIndexBuffer != NULL)
			mIndexBuffer->Release();

		sM2Info.releaseInfo(mModelName);
		return true;
	}

	return false;
}

void M2BatchRender::changeInstancePosition(ui32 id, const Vertex3F& position) {
	mInstanceLoader.changeInstancePosition(id, position);
}